async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});

console.log('script end');

/**
 * 输出顺序：
 * script start
 * async1 start
 * async2
 * promise1
 * script end
 * async1 end
 * promise2
 * setTimeout
*/

/**
 * why
 * 事件循环 Event Loop：
 * Js 是一个单线程语言，它在主线程上无限循环执行，这个过程，被称为同步任务。
 * 但是，一次循环的过程中，有可能会遇到许多需要延时执行的任务，但主线程不可能等待它这些任务的时机到达再执行，这样会造成阻塞，从而导致卡死
 * 于是，便有了 任务（/消息）队列 task queue
 *    - 任务队列分为多种，主要分为4 种，且优先级不同：
 *    -- 延时队列：用于存放计时器到达后的回调任务，优先级「中」
 *    -- 交互队列：用于存放用户操作后产生的事件处理任务，优先级「高」
 *    -- 微队列：用户存放需要最快执行的任务，优先级「最高」
 *    -- 其他
 * 当一次 Event Loop 过程中，再它的执行过程中，如果遇到需要延时执行的任务时，会把它放到对应的任务队列中，然后再继续执行后面的代码，这个过程叫 宏任务 (marco)task；
 * 宏任务执行完毕后，会检查 任务队列 中是否有需要执行的任务（这些任务统称为微任务 microtask），如果有，则依次执行所有 任务队列 中的微任务。
 * 
 * 参考链接：
 * 从一道题浅说 JavaScript 的事件循环 [https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7]
 * 渡一大师课-事件循环原理
 * */ 

/**
 * 解析：
 * 当开始解析时，是执行宏任务
 * 开头的函数 async1，async2 未执行，按顺序下来就打印 script start
 * 接下来，遇到定时器，就放到了 任务队列中的延时队列中
 * 再接下来，执行了 async1()， async1 的第一句就是 打印 async1 start
 * 后面的 await，是表示 await 跟着的表达式会先执行一边，打印 async2, await 下一行的代码放到任务队列中，然后跳出整个 async 函数，执行后面的代码
 * 接下来，遇到 new Promise，便执行，打印 promise1，后面 有 resolve(), 返回的是 promise ,加入到任务队列中，
 * 跟着，再往下执行宏任务，打印 script end
 * 自此，宏任务结束，任务队列中有 微队列：[ fn: console.log('async1 end')， fn: console.log('promise2')]， 延时队列：[ fn：setTimeout]
 * 微任务按照优先级，依次执行，打印：
 * async1 end
 * promise2
 * setTimeout
 * */ 